فارسی

کلاس‌های انتزاعی تایپ‌اسکریپت، مزایا و الگوهای پیشرفته پیاده‌سازی جزئی را برای افزایش قابلیت استفاده مجدد و انعطاف‌پذیری کد در پروژه‌های پیچیده کاوش کنید. شامل مثال‌های عملی.

کلاس‌های انتزاعی تایپ‌اسکریپت: تسلط بر الگوهای پیاده‌سازی جزئی

کلاس‌های انتزاعی یک مفهوم بنیادی در برنامه‌نویسی شیءگرا (OOP) هستند که طرحی کلی برای کلاس‌های دیگر فراهم می‌کنند. در تایپ‌اسکریپت، کلاس‌های انتزاعی مکانیزم قدرتمندی برای تعریف عملکردهای مشترک ارائه می‌دهند در حالی که الزامات پیاده‌سازی مشخصی را بر کلاس‌های مشتق‌شده اعمال می‌کنند. این مقاله به پیچیدگی‌های کلاس‌های انتزاعی تایپ‌اسکریپت می‌پردازد، با تمرکز بر الگوهای عملی برای پیاده‌سازی جزئی، و اینکه چگونه می‌توانند به طور قابل توجهی قابلیت استفاده مجدد، قابلیت نگهداری و انعطاف‌پذیری کد را در پروژه‌های شما افزایش دهند.

کلاس‌های انتزاعی چه هستند؟

یک کلاس انتزاعی در تایپ‌اسکریپت کلاسی است که نمی‌توان مستقیماً از آن نمونه‌سازی کرد. این کلاس به عنوان یک کلاس پایه برای کلاس‌های دیگر عمل می‌کند و مجموعه‌ای از خصوصیات و متدها را تعریف می‌کند که کلاس‌های مشتق‌شده باید پیاده‌سازی (یا بازنویسی) کنند. کلاس‌های انتزاعی با استفاده از کلمه کلیدی abstract تعریف می‌شوند.

ویژگی‌های کلیدی:

چرا از کلاس‌های انتزاعی استفاده کنیم؟

کلاس‌های انتزاعی چندین مزیت در توسعه نرم‌افزار ارائه می‌دهند:

مثال پایه کلاس انتزاعی

بیایید با یک مثال ساده شروع کنیم تا سینتکس پایه یک کلاس انتزاعی در تایپ‌اسکریپت را نشان دهیم:


abstract class Animal {
 abstract makeSound(): string;

 move(): void {
 console.log("Moving...");
 }
}

class Dog extends Animal {
 makeSound(): string {
 return "Woof!";
 }
}

class Cat extends Animal {
 makeSound(): string {
 return "Meow!";
 }
}

//const animal = new Animal(); // Error: Cannot create an instance of an abstract class.

const dog = new Dog();
console.log(dog.makeSound()); // Output: Woof!
dog.move(); // Output: Moving...

const cat = new Cat();
console.log(cat.makeSound()); // Output: Meow!
cat.move(); // Output: Moving...

در این مثال، Animal یک کلاس انتزاعی با یک متد انتزاعی makeSound() و یک متد مشخص move() است. کلاس‌های Dog و Cat از Animal ارث‌بری کرده و پیاده‌سازی‌های مشخصی برای متد makeSound() ارائه می‌دهند. توجه داشته باشید که تلاش برای نمونه‌سازی مستقیم از `Animal` منجر به خطا می‌شود.

الگوهای پیاده‌سازی جزئی

یکی از جنبه‌های قدرتمند کلاس‌های انتزاعی، توانایی تعریف پیاده‌سازی‌های جزئی است. این به شما امکان می‌دهد تا برای برخی متدها پیاده‌سازی پیش‌فرض ارائه دهید در حالی که کلاس‌های مشتق‌شده را ملزم به پیاده‌سازی سایر متدها می‌کنید. این کار تعادلی بین قابلیت استفاده مجدد از کد و انعطاف‌پذیری ایجاد می‌کند.

۱. متدهای انتزاعی با پیاده‌سازی‌های پیش‌فرض در کلاس‌های مشتق‌شده

در این الگو، کلاس انتزاعی یک متد انتزاعی را اعلام می‌کند که *باید* توسط کلاس‌های مشتق‌شده پیاده‌سازی شود، اما هیچ پیاده‌سازی پایه‌ای ارائه نمی‌دهد. این کار کلاس‌های مشتق‌شده را مجبور می‌کند تا منطق خود را ارائه دهند.


abstract class DataProcessor {
 abstract fetchData(): Promise;
 abstract processData(data: any): any;
 abstract saveData(processedData: any): Promise;

 async run(): Promise {
 const data = await this.fetchData();
 const processedData = this.processData(data);
 await this.saveData(processedData);
 }
}

class APIProcessor extends DataProcessor {
 async fetchData(): Promise {
 // پیاده‌سازی برای واکشی داده از یک API
 console.log("Fetching data from API...");
 return { data: "API Data" }; // داده ساختگی
 }

 processData(data: any): any {
 // پیاده‌سازی برای پردازش داده‌های خاص API
 console.log("Processing API data...");
 return { processed: data.data + " - Processed" }; // داده پردازش‌شده ساختگی
 }

 async saveData(processedData: any): Promise {
 // پیاده‌سازی برای ذخیره داده‌های پردازش‌شده در پایگاه داده از طریق API
 console.log("Saving processed API data...");
 console.log(processedData);
 }
}

const apiProcessor = new APIProcessor();
apiProcessor.run();

در این مثال، کلاس انتزاعی DataProcessor سه متد انتزاعی تعریف می‌کند: fetchData()، processData() و saveData(). کلاس APIProcessor از DataProcessor ارث‌بری کرده و پیاده‌سازی‌های مشخصی برای هر یک از این متدها ارائه می‌دهد. متد run() که در کلاس انتزاعی تعریف شده است، کل فرآیند را هماهنگ می‌کند و اطمینان می‌دهد که هر مرحله به ترتیب صحیح اجرا می‌شود.

۲. متدهای مشخص با وابستگی‌های انتزاعی

این الگو شامل متدهای مشخصی در کلاس انتزاعی است که برای انجام وظایف خاص به متدهای انتزاعی متکی هستند. این به شما امکان می‌دهد یک الگوریتم مشترک را تعریف کنید در حالی که جزئیات پیاده‌سازی را به کلاس‌های مشتق‌شده واگذار می‌کنید.


abstract class PaymentProcessor {
 abstract validatePaymentDetails(paymentDetails: any): boolean;
 abstract chargePayment(paymentDetails: any): Promise;
 abstract sendConfirmationEmail(paymentDetails: any): Promise;

 async processPayment(paymentDetails: any): Promise {
 if (!this.validatePaymentDetails(paymentDetails)) {
 console.error("Invalid payment details.");
 return false;
 }

 const chargeSuccessful = await this.chargePayment(paymentDetails);
 if (!chargeSuccessful) {
 console.error("Payment failed.");
 return false;
 }

 await this.sendConfirmationEmail(paymentDetails);
 console.log("Payment processed successfully.");
 return true;
 }
}

class CreditCardPaymentProcessor extends PaymentProcessor {
 validatePaymentDetails(paymentDetails: any): boolean {
 // اعتبارسنجی جزئیات کارت اعتباری
 console.log("Validating credit card details...");
 return true; // اعتبارسنجی ساختگی
 }

 async chargePayment(paymentDetails: any): Promise {
 // شارژ کارت اعتباری
 console.log("Charging credit card...");
 return true; // شارژ ساختگی
 }

 async sendConfirmationEmail(paymentDetails: any): Promise {
 // ارسال ایمیل تأیید برای پرداخت با کارت اعتباری
 console.log("Sending confirmation email for credit card payment...");
 }
}

const creditCardProcessor = new CreditCardPaymentProcessor();
creditCardProcessor.processPayment({ cardNumber: "1234-5678-9012-3456", expiryDate: "12/24", cvv: "123", amount: 100 });

در این مثال، کلاس انتزاعی PaymentProcessor یک متد processPayment() را تعریف می‌کند که منطق کلی پردازش پرداخت را مدیریت می‌کند. با این حال، متدهای validatePaymentDetails()، chargePayment() و sendConfirmationEmail() انتزاعی هستند و کلاس‌های مشتق‌شده را ملزم به ارائه پیاده‌سازی‌های مشخص برای هر روش پرداخت (مثلاً کارت اعتباری، پی‌پال و غیره) می‌کنند.

۳. الگوی متد الگو (Template Method)

الگوی متد الگو (Template Method) یک الگوی طراحی رفتاری است که اسکلت یک الگوریتم را در کلاس انتزاعی تعریف می‌کند اما به زیرکلاس‌ها اجازه می‌دهد تا مراحل خاصی از الگوریتم را بدون تغییر ساختار آن بازنویسی (override) کنند. این الگو به ویژه زمانی مفید است که شما دنباله‌ای از عملیات دارید که باید به ترتیب خاصی انجام شوند، اما پیاده‌سازی برخی از عملیات ممکن است بسته به زمینه متفاوت باشد.


abstract class ReportGenerator {
 abstract generateHeader(): string;
 abstract generateBody(): string;
 abstract generateFooter(): string;

 generateReport(): string {
 const header = this.generateHeader();
 const body = this.generateBody();
 const footer = this.generateFooter();

 return `${header}\n${body}\n${footer}`;
 }
}

class PDFReportGenerator extends ReportGenerator {
 generateHeader(): string {
 return "PDF Report Header";
 }

 generateBody(): string {
 return "PDF Report Body";
 }

 generateFooter(): string {
 return "PDF Report Footer";
 }
}

class CSVReportGenerator extends ReportGenerator {
 generateHeader(): string {
 return "CSV Report Header";
 }

 generateBody(): string {
 return "CSV Report Body";
 }

 generateFooter(): string {
 return "CSV Report Footer";
 }
}

const pdfReportGenerator = new PDFReportGenerator();
console.log(pdfReportGenerator.generateReport());

const csvReportGenerator = new CSVReportGenerator();
console.log(csvReportGenerator.generateReport());

در اینجا، `ReportGenerator` فرآیند کلی تولید گزارش را در متد `generateReport()` تعریف می‌کند، در حالی که مراحل جداگانه (سربرگ، بدنه، پاورقی) به زیرکلاس‌های مشخص `PDFReportGenerator` و `CSVReportGenerator` واگذار شده است.

۴. خصوصیات (Properties) انتزاعی

کلاس‌های انتزاعی همچنین می‌توانند خصوصیات انتزاعی تعریف کنند، که خصوصیاتی هستند که باید در کلاس‌های مشتق‌شده پیاده‌سازی شوند. این برای اعمال حضور عناصر داده‌ای خاص در کلاس‌های مشتق‌شده مفید است.


abstract class Configuration {
 abstract apiKey: string;
 abstract apiUrl: string;

 getFullApiUrl(): string {
 return `${this.apiUrl}/${this.apiKey}`;
 }
}

class ProductionConfiguration extends Configuration {
 apiKey: string = "prod_api_key";
 apiUrl: string = "https://api.example.com/prod";
}

class DevelopmentConfiguration extends Configuration {
 apiKey: string = "dev_api_key";
 apiUrl: string = "http://localhost:3000/dev";
}

const prodConfig = new ProductionConfiguration();
console.log(prodConfig.getFullApiUrl()); // Output: https://api.example.com/prod/prod_api_key

const devConfig = new DevelopmentConfiguration();
console.log(devConfig.getFullApiUrl()); // Output: http://localhost:3000/dev/dev_api_key

در این مثال، کلاس انتزاعی Configuration دو خصوصیت انتزاعی تعریف می‌کند: apiKey و apiUrl. کلاس‌های ProductionConfiguration و DevelopmentConfiguration از Configuration ارث‌بری کرده و مقادیر مشخصی برای این خصوصیات ارائه می‌دهند.

ملاحظات پیشرفته

میکسین‌ها (Mixins) با کلاس‌های انتزاعی

تایپ‌اسکریپت به شما امکان می‌دهد تا کلاس‌های انتزاعی را با میکسین‌ها ترکیب کنید تا کامپوننت‌های پیچیده‌تر و قابل استفاده‌ مجدد ایجاد کنید. میکسین‌ها راهی برای ساختن کلاس‌ها با ترکیب قطعات کوچک و قابل استفاده مجدد از عملکردها هستند.


// تعریف یک نوع برای سازنده یک کلاس
type Constructor = new (...args: any[]) => T;

// تعریف یک تابع میکسین
function Timestamped(Base: TBase) {
 return class extends Base {
 timestamp = new Date();
 };
}

// یک تابع میکسین دیگر
function Logged(Base: TBase) {
 return class extends Base {
 log(message: string) {
 console.log(`${this.constructor.name}: ${message}`);
 }
 };
}

abstract class BaseEntity {
 abstract id: number;
}

// اعمال میکسین‌ها به کلاس انتزاعی BaseEntity
const TimestampedEntity = Timestamped(BaseEntity);
const LoggedEntity = Logged(TimestampedEntity);

class User extends LoggedEntity {
 id: number = 123;
 name: string = "John Doe";

 constructor() {
 super();
 this.log("User created");
 }
}

const user = new User();
console.log(user.id); // Output: 123
console.log(user.timestamp); // Output: Current timestamp
user.log("User updated"); // Output: User: User updated

این مثال میکسین‌های Timestamped و Logged را با کلاس انتزاعی BaseEntity ترکیب می‌کند تا کلاس User را ایجاد کند که عملکرد هر سه را به ارث می‌برد.

تزریق وابستگی (Dependency Injection)

کلاس‌های انتزاعی می‌توانند به طور مؤثر با تزریق وابستگی (DI) برای جداسازی کامپوننت‌ها و بهبود قابلیت تست‌پذیری استفاده شوند. شما می‌توانید کلاس‌های انتزاعی را به عنوان رابط‌هایی برای وابستگی‌های خود تعریف کرده و سپس پیاده‌سازی‌های مشخص را به کلاس‌های خود تزریق کنید.


abstract class Logger {
 abstract log(message: string): void;
}

class ConsoleLogger extends Logger {
 log(message: string): void {
 console.log(`[Console]: ${message}`);
 }
}

class FileLogger extends Logger {
 log(message: string): void {
 // پیاده‌سازی برای لاگ‌گیری در یک فایل
 console.log(`[File]: ${message}`);
 }
}

class AppService {
 private logger: Logger;

 constructor(logger: Logger) {
 this.logger = logger;
 }

 doSomething() {
 this.logger.log("Doing something...");
 }
}

// تزریق ConsoleLogger
const consoleLogger = new ConsoleLogger();
const appService1 = new AppService(consoleLogger);
appService1.doSomething();

// تزریق FileLogger
const fileLogger = new FileLogger();
const appService2 = new AppService(fileLogger);
appService2.doSomething();

در این مثال، کلاس AppService به کلاس انتزاعی Logger وابسته است. پیاده‌سازی‌های مشخص (ConsoleLogger، FileLogger) در زمان اجرا تزریق می‌شوند، که به شما امکان می‌دهد به راحتی بین استراتژی‌های مختلف لاگ‌گیری جابجا شوید.

بهترین شیوه‌ها (Best Practices)

نتیجه‌گیری

کلاس‌های انتزاعی تایپ‌اسکریپت ابزاری قدرتمند برای ساخت برنامه‌های کاربردی قوی و قابل نگهداری هستند. با درک و به کارگیری الگوهای پیاده‌سازی جزئی، می‌توانید از مزایای کلاس‌های انتزاعی برای ایجاد کدی انعطاف‌پذیر، قابل استفاده مجدد و با ساختار خوب بهره‌مند شوید. از تعریف متدهای انتزاعی با پیاده‌سازی‌های پیش‌فرض گرفته تا استفاده از کلاس‌های انتزاعی با میکسین‌ها و تزریق وابستگی، امکانات بسیار گسترده است. با پیروی از بهترین شیوه‌ها و بررسی دقیق انتخاب‌های طراحی خود، می‌توانید به طور مؤثر از کلاس‌های انتزاعی برای افزایش کیفیت و مقیاس‌پذیری پروژه‌های تایپ‌اسکریپت خود استفاده کنید.

چه در حال ساخت یک برنامه کاربردی سازمانی در مقیاس بزرگ باشید و چه یک کتابخانه ابزار کوچک، تسلط بر کلاس‌های انتزاعی در تایپ‌اسکریپت بدون شک مهارت‌های توسعه نرم‌افزار شما را بهبود می‌بخشد و شما را قادر می‌سازد تا راه‌حل‌های پیچیده‌تر و قابل نگهداری‌تری ایجاد کنید.